/*
  launchino.c - Partial implementation of the Wiring API for the MSP430.

  Copyright (c) 2010 JP Lopez

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA

*/

#include "launchino.h"
#include <io.h>
#include <signal.h>

boolean ADCDone;
uint16_t micros_us;
uint16_t micros_fraction;
uint32_t millis_from_powerup;
func_ptr interrupt_functions[DIGITAL_PIN_COUNT];

/* Modify according to your needs */
void init(void) {
    micros_us = 0;
    micros_fraction = 0;
    millis_from_powerup = 0;

    configInterrupts();

    WDTCTL = WDTPW + WDTHOLD;
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;

    __bis_SR_register(GIE);
}

void pinMode(uint8_t pin, uint8_t mode) {
	uint8_t digital_pins[] = {BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7};
	
	if (pin<FIRST_DIGITAL_PIN || pin>LAST_DIGITAL_PIN) return;
	
	if (mode == INPUT) {
		P1DIR &= ~(1<<digital_pins[pin]);
	}
	else {
		P1DIR |= (1<<digital_pins[pin]);
	}
}

void digitalWrite(uint8_t pin, uint8_t state) {
	uint8_t digital_pins[] = {BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7};
	
	if (pin<FIRST_DIGITAL_PIN || pin>LAST_DIGITAL_PIN) return;
	
	if (state == LOW) {
		P1OUT &= ~(1<<digital_pins[pin]);
	}
	else {
		P1OUT |= (1<<digital_pins[pin]);
	}
}

uint8_t digitalRead(uint8_t pin) {
	uint8_t digital_mask[] = {BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7};
	uint8_t pinb_status;
	uint8_t pin_mask;
	
	if (pin<FIRST_DIGITAL_PIN || pin>LAST_DIGITAL_PIN) return (LOW);
	
	pinb_status = P1IN;
	pin_mask = digital_mask[pin];
	
	if (pinb_status & pin_mask) return (HIGH);
	else return (LOW);
}

uint16_t analogRead(uint8_t pin) {
    uint16_t analog_channels[] = {INCH_0, INCH_1, INCH_2, INCH_3, INCH_4, INCH_5, INCH_6, INCH_7};
    uint16_t analog_value = 0;
	
    if (pin<FIRST_ANALOG_PIN || pin>LAST_ANALOG_PIN) return (LOW);

    /* Turn off ADC10 */
    ADC10CTL0 &= ~ENC;
    
    /* Select channel */
    ADC10CTL1 = analog_channels[pin];    

    /* Turn on ADC10 */
    ADC10CTL0 |= ENC + ADC10SC;
    
    /* Wait for conversion to finish */
    while(ADC10CTL1 & ADC10BUSY);
    
    analog_value = ADC10MEM;
    
    return (analog_value);    
}

void analogWrite(uint8_t pin, uint8_t value) {
    #warning analogWrite function is not implemented.
    return;
}

void configInterrupts(void) {
    uint8_t i;
    
    for (i=FIRST_DIGITAL_PIN; i<LAST_DIGITAL_PIN; i++) {
        interrupt_functions[i] = null;
    }
    
    P1IFG = 0x00;
}

void attachInterrupt(uint8_t pin, void (*function)(void), uint8_t mode) {
    uint8_t digital_pins[] = {BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7};
    
    if (pin<FIRST_DIGITAL_PIN || pin>LAST_DIGITAL_PIN) return;
	
    interrupt_functions[pin] = function;
	
    P1IE |= (1<<digital_pins[pin]);
	
    if (mode == RISING) {
        P1IES &= ~(1<<digital_pins[pin]);
    }
    else {
        P1IES |= (1<<digital_pins[pin]);
    }
	
    P1IFG &= ~(1<<digital_pins[pin]);
}

void detachInterrupt(uint8_t pin) {
    uint8_t digital_pins[] = {BIT0, BIT1, BIT2, BIT3, BIT4, BIT5, BIT6, BIT7};

    if (pin<FIRST_DIGITAL_PIN || pin>LAST_DIGITAL_PIN) return;

    interrupt_functions[pin] = null;

    P1IE &= ~(1<<digital_pins[pin]);

    P1IFG &= ~(1<<digital_pins[pin]);
}

/* PORT 1 ISR: PIN 0 has highest priority, PIN 7 has lowest */
interrupt (PORT1_VECTOR) PORT1_ISR(void) {
    uint8_t interrupt_status = P1IFG;
    uint8_t i, j;
    void (*f)(void) = null;
    
    for (i = 0x01, j = 0; i ; i=i<<1, j++) {
        if (interrupt_status & i) {
            f = interrupt_functions[j];
            break;
        }
    }
    
    /* Clear interrupt, try to serve other possible interrupts */
    //P1IFG &= ~(i);

    /* Clear ALL interrupts, if there are remaining interrupts they will not be served */
    P1IFG = 0x00;

    /* Call function associated to pin */
    if (f != null) (*f)();
}

/* TIMERA0 ISR: */
interrupt (TIMERA0_VECTOR) TIMERA0_ISR(void) {
    micros_us++;
    micros_fraction++;
    if (micros_fraction > 999) {
        millis_from_powerup++;
        micros_fraction = 0;
    }
}

uint32_t millis(void) {
    return (millis_from_powerup);
}

uint16_t micros(void) {
    return (micros_us);
}

void delay(uint32_t ms) {
    uint32_t start = millis();
    while (millis() - start <= ms);
}

void delayMicroseconds(uint16_t us) {
    micros_us = 0;
    while (micros() < us);
}

